

#ifndef __COMM_SERIAL_H__
#define __COMM_SERIAL_H__

#include "comm/comm_base.h"
#include <iostream>
#include <string.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <list>
#include <vector>

#include <pthread.h>
#include <fcntl.h>

using namespace std;

#pragma pack(1)

/**************************************************************************************
功能描述: 串口类
修改记录:
**************************************************************************************/
class SERIAL_C
{
public:
    SERIAL_C(void)
    {
        fileDes_ = -1;
    }

    ~SERIAL_C(void)
    {
        Close();
    }

    BOOL  IsValid(void);
    INT32 Open(const char *pcDevName);
    INT32 Open(const char *pcDevName, int speed);
    void  Close(void);
    INT32 ReadData(UINT08 *buf, UINT32 nbytes);
    void  SendData(const UINT08 *pbuf, int len);
    
private:
    INT32 fileDes_;

    int Set(int fd, int nSpeed, int nBits, char nEvent, int nStop);
    int tread(int fd, UINT08 *buf, size_t nbytes, unsigned int timout);
};

/**************************************************************************************
功能描述: 消息处理基类
修改记录:
**************************************************************************************/
class MSG_PROC_BASE_C
{
public:
    virtual void ProcMsg(UINT08 srcAddr, UINT08 msgType, UINT08 *pMsg, UINT32 msgLen) = 0;
private:
};

/**************************************************************************************
功能描述: 消息转发基类
修改记录:
**************************************************************************************/
class MSG_PASS_BASE_C
{
public:
    virtual void Send(UINT08 tgtAddr, const UINT08 *pMsg, UINT32 msgLen) = 0;
private:
};

/**************************************************************************************
功能描述: 出口读取超时时长，单位us
修改记录:
**************************************************************************************/
#define SERIAL_READ_TIMEOUT_LEN                     (1 * 1000)

/**************************************************************************************
功能描述: 中断消息等待后续内容超时读取次数
          消息发送字节间时间间隔5ms
修改记录:
**************************************************************************************/
#define SERIAL_MSG_SEND_BYTE_INTERVAL               0.005

/**************************************************************************************
功能描述: ACK等待最大时长，单位s
修改记录:
**************************************************************************************/
#define SERIAL_WAIT_ACK_FRAME_MAX_TIME_LEN          0.1
//#define SERIAL_WAIT_ACK_FRAME_MAX_TIME_LEN          0.2       /* 转发的时候要时间长点 */

/**************************************************************************************
功能描述: 握手间隔时长
          如果持续这么长的时间无发送消息，则发送握手消息
修改记录:
**************************************************************************************/
#define SERIAL_MSG_HANDSHAKE_INTERVAL_TIME          1.0

/**************************************************************************************
功能描述: 判定串口异常的时间
          如果持续这么长的时间无法送成功消息，包括握手消息，则判定串口异常
          采用3次握手间隔时长
修改记录:
**************************************************************************************/
#define SERIAL_NO_SEND_SUCC_TIME_OF_JUDGE_ERROR     (SERIAL_MSG_HANDSHAKE_INTERVAL_TIME * 3 + 0.1)

/**************************************************************************************
功能描述: 串口消息最大重发次数
修改记录:
**************************************************************************************/
#define SERIAL_MSG_REPEAT_SEND_MAX_TIMES            3

/**************************************************************************************
功能描述: 发送缓存消息数
修改记录:
**************************************************************************************/
#define SERIAL_SNED_MSG_BUFF_MAX_NUM                5

/**************************************************************************************
功能描述: 串口消息起始标志
修改记录:
**************************************************************************************/
#define SERIAL_MSG_BEGIN_MARK               0xAA55

enum SERIAL_ADDR_ENUM {
    SERIAL_ADDR_BOARDCAST                   = 0x00, /* 广播，所有设备接收 */
    SERIAL_ADDR_NET_CTRL                    = 0x01, /* 调度系统 */
    SERIAL_ADDR_MMIS                        = 0x02, /* 人机系统 */
    SERIAL_ADDR_NAV                         = 0x03, /* 导航控制系统 */
    SERIAL_ADDR_DCU                         = 0x04, /* DCU控制板 */
    SERIAL_ADDR_POWER_CTRL                  = 0x05, /* 电源控制板 */
    SERIAL_ADDR_CHARGER_CTRL                = 0x06, /* 充电控制板 */
    SERIAL_ADDR_DOOR_CTRL                   = 0x07, /* 开关门控制板 */
    SERIAL_ADDR_VISION                      = 0x08, /* 视觉系统 */
    SERIAL_ADDR_BUTT,                               /* 无效地址 *//* 所有有效地址必须在此之前定义 */
};

enum SERIAL_MSG_FRAME_TYPE_ENUM
{
    SERIAL_MSG_FRAME_TYPE_ACK_SUCC          = 0x00, /* 接收成功应答帧 */
    SERIAL_MSG_FRAME_TYPE_ACK_FAIL          = 0x01, /* 接收失败应答帧 */
    SERIAL_MSG_FRAME_TYPE_HANDSHAKE         = 0x02, /* 握手帧 */
    SERIAL_MSG_FRAME_TYPE_REQ               = 0x03, /* 原始请求帧 */
};

enum SERIAL_MSG_TYPE_ENUM
{
    SERIAL_MSG_TYPE_COMM_MIN                = 0xA0, /* 公共消息最小值 */
    SERIAL_MSG_TYPE_QUERY_WORK_MODE         = 0xA4, /* 查询工作模式 */
    SERIAL_MSG_TYPE_SET_WORK_MODE           = 0xA5, /* 设置工作模式 */
    SERIAL_MSG_TYPE_QUERY_DEV_INFO          = 0xA6, /* 查询设备信息 */
    SERIAL_MSG_TYPE_SEND_MAIN_DEV_UPG_DATA  = 0xA7, /* 升级主固件(DSP)信息 */
    SERIAL_MSG_TYPE_SEND_OTHER_DEV_UPG_DATA = 0xA8, /* 升级次固件(FPGA)信息 */
    SERIAL_MSG_TYPE_CHECK_MAIN_DEV          = 0xA9, /* 校验固件信息，主固件(DPS) */
    SERIAL_MSG_TYPE_CHECK_OTHER_DEV         = 0xAA, /* 校验固件信息，次固件(FPGA) */
};

/**************************************************************************************
功能描述: 通信消息格式
          帧头   起始标志     2字节 0x55AA
                 数据段长度   2字节 0~512
          命令段 源地址       1字节
                 目标地址     1字节
                 帧类型       1字节
                 功能码       1字节
                 帧编号       1字节
          数据段 消息内容     n字节
          校验端 CRC16        2字节

          字节序采用小端，由于本地主机就是小端序，所以不用转换字节序。
修改记录:
**************************************************************************************/
typedef struct {
    UINT16 beginMark;               /* 定义详见 SERIAL_MSG_BEGIN_MARK */
    UINT16 dataLen;
    UINT08 srcAddr;                 /* 定义详见 SERIAL_ADDR_ENUM */
    UINT08 tgtAddr;                 /* 定义详见 SERIAL_ADDR_ENUM */
    UINT08 frameType;               /* 定义详见 SERIAL_MSG_FRAME_TYPE_ENUM */
    UINT08 msgType;                 /* 定义详见 SERIAL_MSG_TYPE_ENUM */
    UINT08 frameNo;
} SERIAL_MSG_HEAD_STRU;

typedef struct {
    UINT16 crc16;
} SERIAL_MSG_TAIL_STRU;

typedef struct {
    SERIAL_MSG_HEAD_STRU stHead;
    SERIAL_MSG_TAIL_STRU stTail;
} SERIAL_MSG_ACK_FRAME_STRU;

/**************************************************************************************
功能描述: 消息相关长度
修改记录:
**************************************************************************************/
#define SERIAL_MSG_DATA_MAX_LEN     512
#define SERIAL_MSG_MIN_LEN          (sizeof(SERIAL_MSG_HEAD_STRU) + sizeof(SERIAL_MSG_TAIL_STRU))
#define SERIAL_MSG_MAX_LEN          (SERIAL_MSG_MIN_LEN + SERIAL_MSG_DATA_MAX_LEN)

/**************************************************************************************
功能描述: 按地址维护帧号
修改记录:
**************************************************************************************/
typedef struct {
    UINT08 lastSendFrameNo;
    UINT08 lastRecvFrameNo;
} SERIAL_ADDR_FRAME_INFO_STRU;

typedef struct {
    UINT32 msgLen;
    UINT08 aucMsg[SERIAL_MSG_MAX_LEN];
} SERIAL_SEND_MSG_STRU;

/**************************************************************************************
功能描述: 发送消息缓存
          发送消息按照顺序存放到列表中，列表循环使用
修改记录:
**************************************************************************************/
class SERIAL_SEND_MSG_BUFF_C
{
public:
    SERIAL_SEND_MSG_BUFF_C(void)
    {
        idleBuffIdx_         = 0;
        sendingMsgIdx_       = 0;
        sendingMsgSendTimes_ = 0;
        memset(astMsg_, 0x00, sizeof(astMsg_));
    }

    SERIAL_SEND_MSG_STRU *AllocMsg(void);
    SERIAL_SEND_MSG_STRU *GetSendingMsg(void);
    void                  FreeSendingMsg(void);
    
    void                  AddSendingMsgSendTimes(void);
    BOOL                  IsSendingMsgSendMaxTimes(void);
    
private:
    UINT08               idleBuffIdx_;
    UINT08               sendingMsgIdx_;
    UINT08               sendingMsgSendTimes_;
    SERIAL_SEND_MSG_STRU astMsg_[SERIAL_SNED_MSG_BUFF_MAX_NUM];
};

/**************************************************************************************
功能描述: 串口发送消息统计
修改记录:
**************************************************************************************/
typedef struct SERIAL_SEND_DBG_INFO_STRU {
    UINT32 msgCount;                        /* 发送消息数 */
    UINT32 succMsgLenSum;                   /* 消息总长 */
    UINT32 passMsgCoun;
    UINT32 frameFailCountForErrCheck;       /* 对方回复校验失败的发送帧数 */
    UINT32 frameFailCountForRspTimeout;     /* 对方回复超时的发送帧数 */
    UINT32 msgFailCountForBusy;             /* 因正在发送其它消息导致消息发送失败数 */
    UINT32 msgFailCountForTimesOver;        /* 因多次发送失败导致消息发送失败次数 */

    SERIAL_SEND_DBG_INFO_STRU(void)
    {
        msgCount                     = 0;
        succMsgLenSum                = 0;
        passMsgCoun                  = 0;
        frameFailCountForErrCheck    = 0;
        frameFailCountForRspTimeout  = 0;
        msgFailCountForBusy          = 0;
        msgFailCountForTimesOver     = 0;
    }
    
    void Output(void)
    {
        float failForBusyRate      = (msgCount == 0)   ? 0.0 : (float(msgFailCountForBusy)              / float(msgCount));
        UINT32 trySendMsgCount     = msgCount - msgFailCountForBusy;
        float frameCheckFailRate   = (trySendMsgCount == 0) ? 0.0 : (float(frameFailCountForErrCheck)   / float(trySendMsgCount));
        float frameAckTimeoutRate  = (trySendMsgCount == 0) ? 0.0 : (float(frameFailCountForRspTimeout) / float(trySendMsgCount));            
        float failForTimesOverRate = (trySendMsgCount == 0) ? 0.0 : (float(msgFailCountForTimesOver)    / float(trySendMsgCount));

        ST_LOG_INFO("MsgCount=%u, MsgLen=%u, passMsgCoun=%u, FailForBusy.count=%u, .rate=%f, CheckFail.count=%u, .rate=%f, Timeout.count=%u, .rate=%f, FailForTimesOver.count=%u, .rate=%f.",
                    msgCount,                    succMsgLenSum,
                    passMsgCoun,
                    msgFailCountForBusy,         failForBusyRate,
                    frameFailCountForErrCheck,   frameCheckFailRate,
                    frameFailCountForRspTimeout, frameAckTimeoutRate,
                    msgFailCountForTimesOver,    failForTimesOverRate);

        msgCount                    = 0;
        succMsgLenSum               = 0;
        passMsgCoun                 = 0;
        frameFailCountForErrCheck   = 0;
        frameFailCountForRspTimeout = 0;
        msgFailCountForBusy         = 0;
        msgFailCountForTimesOver    = 0;
    }
} SERIAL_SEND_DBG_INFO_STRU;

/**************************************************************************************
功能描述: 串口接收消息统计
修改记录:
**************************************************************************************/
typedef struct SERIAL_RECV_DBG_INFO_STRU {
    UINT32 abnormalCount;
    UINT32 discardDataLen;
    UINT32 discardDataLenMax;
    UINT32 msgCount;
    UINT32 succMsgLenSum;               /* 消息总长 */
    UINT32 passMsgCount;
    UINT32 msgCheckFailCount;
    float  msgCheckFailRateMax;

    SERIAL_RECV_DBG_INFO_STRU(void)
    {
        abnormalCount   = 0;
        discardDataLen      = 0;
        discardDataLenMax   = 0;
        msgCount            = 0;
        succMsgLenSum       = 0;
        passMsgCount        = 0;
        msgCheckFailCount   = 0;
        msgCheckFailRateMax = 0;
    }

    void Output(void)
    {
        discardDataLenMax   = MAX(discardDataLenMax, discardDataLen);
        float checkFailRate = (msgCount == 0) ? 0 : float(msgCheckFailCount) / float(msgCount);
        msgCheckFailRateMax = MAX(msgCheckFailRateMax, checkFailRate);

        ST_LOG_INFO("AbnormalCount=%u, msgCount=%u, MsgLen=%u, passMsgCount=%u, DiscardDataLen=%u, DiscardDataLenMax=%u, CheckFailRate=%f, MaxCheckFailRate=%f.", 
                    abnormalCount, 
                    msgCount,       succMsgLenSum,
                    passMsgCount,
                    discardDataLen, discardDataLenMax, 
                    checkFailRate,  msgCheckFailRateMax);

        abnormalCount     = 0;
        discardDataLen    = 0;
        msgCount          = 0;
        succMsgLenSum     = 0;
        passMsgCount      = 0;
        msgCheckFailCount = 0;
    }
} SERIAL_RECV_DBG_INFO_STRU;

/**************************************************************************************
功能描述: 串口管理
          注意，当前一个进程只能有一个该对象
修改记录:
**************************************************************************************/
class SERIAL_MNG_C
{
public:
    SERIAL_MNG_C(void)
    {
        tid_                     = 0;
        
        pobjReceiveMsg_          = NULL;
        pobjPassMsg_             = NULL;
        isComPortError_          = false;

        memset(astAddrFrameInfo_, 0xFF, sizeof(astAddrFrameInfo_));
        curMsgFrameSendTimeSec_  = 0;
    }

    ~SERIAL_MNG_C(void)
    {
        DeleteThreadTsk();
        objSerial_.Close();
    }

    INT32 Init(SERIAL_ADDR_ENUM enLocAddr, SERIAL_ADDR_ENUM enPeerAddr, const char *pcDevName, MSG_PROC_BASE_C *pobjMsgProc, MSG_PASS_BASE_C *pobjMsgPass = NULL);
    bool  IsConnectFault(void);
    INT32 Send(SERIAL_ADDR_ENUM enTgtAddr, UINT08 msgType, const UINT08 *pMsg, UINT32 msgLen);
    INT32 Pass(const UINT08 *pMsg, UINT32 msgLen);
    
private:
    pthread_t                           tid_;

    static SERIAL_C                     objSerial_;
    static MSG_PROC_BASE_C             *pobjReceiveMsg_;
    static MSG_PASS_BASE_C             *pobjPassMsg_;
    static bool                         isComPortError_;
    static SERIAL_ADDR_ENUM             enLocAddr_;
    static SERIAL_ADDR_ENUM             enPeerAddr_;

    static SERIAL_ADDR_FRAME_INFO_STRU  astAddrFrameInfo_[SERIAL_ADDR_BUTT];    /* 各个收发地址对应的最后一个帧编号 */
    
    static pthread_mutex_t              sendMutex_;
    static SERIAL_SEND_MSG_BUFF_C       objSendMsgBuffMng_;                     /* 加锁使用 */
    static double                       curMsgFrameSendTimeSec_;                /* 加锁使用 *//* 消息发送的时间秒，计算等待应答超时时间，0表示没有处理等待应答的状态 */
    static double                       lastTryToSendMsgTimeSec_;               /* 上一次尝试发送消息的时间秒 */
    static double                       lastSendMsgSuccTimeSec_;                /* 上一次成功发送消息的时间秒 */

    static SERIAL_SEND_DBG_INFO_STRU    stSendDbgInfo_;
    static SERIAL_RECV_DBG_INFO_STRU    stRecvDbgInfo_;

    static UINT16                       ccittTable_[256];

    INT32          CreateThreadTsk(void);
    void           DeleteThreadTsk(void);
    
    static UINT16  GetCRC16(const UINT08 *pucData, UINT32 dataLen, unsigned long oldCRC16);
    static UINT08  AllocSendFrameNo(UINT08 tgtAddr);
    static BOOL    IsRecvFrameNoRepeat(UINT08 srcAddr, UINT08 recvFrameNo);
    
    static void    UptComPortState(double curTimeSec);
    static void    OutputDbgInfo(double curTimeSec);
    
    static void    FillSendMsg(SERIAL_SEND_MSG_STRU *pstMsgBuff, SERIAL_ADDR_ENUM enTgtAddr, SERIAL_MSG_FRAME_TYPE_ENUM enFrameType, UINT08 msgType, const UINT08 *pMsg, UINT32 msgLen);
    static void    SendBuffMsg(double curTimeSec);
    static INT32   SendMsg(SERIAL_ADDR_ENUM enTgtAddr, SERIAL_MSG_FRAME_TYPE_ENUM enFrameType, UINT08 msgType, const UINT08 *pMsg, UINT32 msgLen);
    static void    SendHandshakeMsg(void);
    static void    Handshake(double curTimeSec);
    static void    SendAckFrame(const SERIAL_MSG_HEAD_STRU &stReqFrameHead, BOOL isCheckSucc);

    static BOOL    IsAckFrame(const SERIAL_MSG_HEAD_STRU &stFrameHead);
    static BOOL    IsFrameNeedAppProc(const SERIAL_MSG_HEAD_STRU &stFrameHead);
    static BOOL    IsFrameNeedAck(const SERIAL_MSG_HEAD_STRU &stFrameHead);
    static void    ProcAckFrame(double curTimeSec, const SERIAL_MSG_HEAD_STRU &stAckFrameHead);
    static void    ProcAckFrameTimeout(double curTimeSec);
    static void    ProcFrame(double curTimeSec, const SERIAL_MSG_HEAD_STRU &stHead, UINT08 *pucMsgCnt);
    static UINT32  ProcRecvData(double curTimeSec, UINT08 *pucData, UINT32 dataLen);
    static void   *SerialRecvTask(void *arg);
};

#pragma pack()

#endif

